# -*- coding: utf-8 -*-
# created on 2017/03/14
from itertools import chain
from sympy import Union, EmptySet, FiniteSet, S, Max, Min, Interval, oo, floor, ceiling, sympify
from mathsolver.functions.base import (BaseFunction, BaseFunc, BaseEq, BaseVariable, BaseBelong, BaseValue,
                                       BaseSymbolValue, BasePieceFunc, BaseSinEq, new_latex)
from mathsolver.functions.daoshu.hanshudaoshu import HanShuDaoShuYunSuan
from mathsolver.functions.hanshu.base import is_absfunc, is_interval, is_piecefunc, is_constantfunc, is_sumabsfunc
from mathsolver.functions.hanshu.dingyiyu import QiuDingYiYu
from mathsolver.functions.hanshu.helper import (check_inter, my_max_min, yizhizhiyu_qiufanwei,
                                                process_m_eqsolver_output, check_func, limit)
from mathsolver.functions.hanshu.qujueduizhi import QuJueDuiZhi
from mathsolver.functions.hanshu.zuizhi_abs import zuizhi_abs_hancan
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSolve


def interval_bound_val(func, interval, belong=None):
    """
    计算函数在端点处的值
    输出 {duandian1_value: (duandian1, , reacheable1),
         duandian2_value: (duandian2,  reacheable2), ...}
         :param func BaseFunc 类型的函数
         :param interval 区间 Interval/Union 类型
         :param belong optional, 参数的取值范围，用来 elim_sign，因为 limit 求出的结果可能含有 sign(a)
    """
    expr, var = func.expression, func.var

    if isinstance(interval, Interval):
        left, right, left_open, right_open = interval.args

        left_val = limit(expr, var, left, dir="+", assumptions=belong)
        right_val = limit(expr, var, right, dir="-", assumptions=belong)

        return dict(((left_val, (left, not left_open)),
                     (right_val, (right, not right_open))))
    else:
        res = dict()
        for inter in interval.args:
            res.update(interval_bound_val(func, inter))
        return res


def compare_values(values_to_compare, canshu_symbol, canshu_interval, min_max):
    """
    根据端点值和极值，讨论函数何时取到最值，
    比较 f(left), f(right), f(jizhi1), f(jizhi2), ... 的大小

    :param values_to_compare: 类型： {y_vals1: (x_val1, inclusive1), y_vals2: (x_val2, inclusive2), ...}
    :param canshu_symbol 参数符号 sympy 格式
    :param canshu_interval 参数取值范围
    :param min_max Min/Max 指定计算最大值还是最小值
    """
    y_vals = values_to_compare.keys()
    solve_min_max = my_max_min(y_vals, canshu_symbol, canshu_interval, min_max)
    return solve_min_max


def compare_duandian(func, dingyi_interval, canshu_interval, canshu_symbol, min_max):
    """比较函数在区间端点值的大小"""
    result = dict()
    values_to_compare = interval_bound_val(func, dingyi_interval,
                                           BaseBelong([canshu_symbol, "\\in", canshu_interval]))
    # 比较大小
    result_piece = compare_values(values_to_compare, canshu_symbol, canshu_interval, min_max)
    # 分段讨论结果加入到 result 里面
    for _canshu_range, _zuizhi in result_piece:
        result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})
    return result


def compare_duandian_jizhi2(func, dingyi_interval, jizhidian, canshu_symbol, canshu_range, min_max,
                            other_values_to_compare=None):
    """根据端点值和极值，讨论函数何时取到最值，需要讨论极值是否在区间内
    目前只考虑两个极值点，两个以上需要补充"""
    result = dict()
    expr, var = func.expression, func.var
    assert len(jizhidian) == 2, "目前只考虑两个极值点，两个以上需要补充"
    jizhi1, jizhi2 = jizhidian

    jizhi1_inside_qujian = yizhizhiyu_qiufanwei(jizhi1, dingyi_interval).intersect(canshu_range)
    jizhi1_outside_qujian = jizhi1_inside_qujian.complement(canshu_range)
    jizhi2_inside_qujian = yizhizhiyu_qiufanwei(jizhi2, dingyi_interval).intersect(canshu_range)
    jizhi2_outside_qujian = jizhi2_inside_qujian.complement(canshu_range)

    # 比较区间端点处的值
    values_to_compare = interval_bound_val(func, dingyi_interval, BaseBelong([canshu_symbol, "\\in", canshu_range]))

    # 补充：可以比较其他点处的值
    if other_values_to_compare:
        values_to_compare.update(other_values_to_compare)

    # case1: 如果两个极值点都在区间内部，jizhi1, jizhi2 ∈ dingyi_interval，比较4个点大小
    case1_interval = jizhi1_inside_qujian.intersect(jizhi2_inside_qujian)
    if case1_interval:
        values_to_compare.update({expr.subs(var, jizhi1): (jizhi1, True)})
        values_to_compare.update({expr.subs(var, jizhi2): (jizhi2, True)})
        # 比较大小
        result_piece = compare_values(values_to_compare, canshu_symbol, case1_interval, min_max)
        # 分段讨论结果加入到 result 里面
        for _canshu_range, _zuizhi in result_piece:
            result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})

    # case2: jizhi1 在区间内部，jizhi2 在区间外部，比较3个点
    case2_interval = jizhi1_inside_qujian.intersect(jizhi2_outside_qujian)
    if case2_interval:
        values_to_compare.update({expr.subs(var, jizhi1): (jizhi1, True)})
        # 比较大小
        result_piece = compare_values(values_to_compare, canshu_symbol, case2_interval, min_max)
        # 分段讨论结果加入到 result 里面
        for _canshu_range, _zuizhi in result_piece:
            result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})

    # case3: jizhi2 在区间内部，jizhi1 在区间外部，比较3个点
    case3_interval = jizhi2_inside_qujian.intersect(jizhi1_outside_qujian)
    if case3_interval:
        values_to_compare.update({expr.subs(var, jizhi2): (jizhi2, True)})
        # 比较大小
        result_piece = compare_values(values_to_compare, canshu_symbol, case3_interval, min_max)
        # 分段讨论结果加入到 result 里面
        for _canshu_range, _zuizhi in result_piece:
            result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})

    # case4: jizhi1,jizhi2 都在区间外部，比较端点
    case4_interval = jizhi2_outside_qujian.intersect(jizhi1_outside_qujian)
    if case4_interval:
        # 比较大小
        result_piece = compare_values(values_to_compare, canshu_symbol, case4_interval, min_max)
        # 分段讨论结果加入到 result 里面
        for _canshu_range, _zuizhi in result_piece:
            result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})

    return result


def compare_duandian_jizhi(func, dingyi_interval, jizhidian, canshu_symbol, canshu_range, min_max,
                           other_values_to_compare=None):
    """根据端点值和极值，讨论函数何时取到最值，需要讨论极值是否在区间内
    目前只考虑一个极值点，两个以上调用 compare_duandian_jizhi2，三个以上需要补充"""
    result = dict()
    expr, var = func.expression, func.var

    # 补充：如果含有多个极值点（目前只考虑2个），单独讨论
    if isinstance(jizhidian, set):
        return compare_duandian_jizhi2(func, dingyi_interval, jizhidian, canshu_symbol, canshu_range, min_max,
                                       other_values_to_compare)

    # 如果极值点在区间内部，jizhidian ∈ dingyi_interval
    sub_interval = yizhizhiyu_qiufanwei(jizhidian, dingyi_interval)

    values_to_compare = interval_bound_val(func, dingyi_interval,
                                           BaseBelong([canshu_symbol, "\\in", canshu_range]))
    # 补充：可以比较其他点处的值
    if other_values_to_compare:
        values_to_compare.update(other_values_to_compare)

    # 如果极值点始终在区间内部，比较三个点的大小
    if sub_interval == S.Reals:
        values_to_compare.update({expr.subs(var, jizhidian): (jizhidian, True)})

        # 比较大小
        result_piece = compare_values(values_to_compare, canshu_symbol, canshu_range, min_max)
        # 分段讨论结果加入到 result 里面
        for _canshu_range, _zuizhi in result_piece:
            result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})
        return result

    # 如果极值点始终在区间外部，比较端点
    elif sub_interval == EmptySet():
        return compare_duandian(func, dingyi_interval, canshu_range, canshu_symbol, min_max)

    # 讨论极值点在区间内部、外部
    else:
        # 极值点在区间内，比较三个点大小
        # 补充细节 sympy bug：[(-2*E + 1)*exp(-1), exp(-1)] intersect [-(-1 + 2*E)*exp(-1), exp(-1)] 报错
        try:
            canshu_range_inner = canshu_range.intersect(sub_interval)
        except Exception:
            canshu_range_inner = sympify('Interval(%s, %s, %s, %s)' % canshu_range.args).intersect(
                sympify('Interval(%s, %s, %s, %s)' % sub_interval.args))

        if canshu_range_inner:
            values_to_compare.update({expr.subs(var, jizhidian): (jizhidian, True)})

            # 比较大小
            result_piece = compare_values(values_to_compare, canshu_symbol, canshu_range_inner, min_max)
            # 分段讨论结果加入到 result 里面
            for _canshu_range, _zuizhi in result_piece:
                result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})

        # 极值点在区间外，比较端点大小
        canshu_range_outer = canshu_range_inner.complement(canshu_range)
        if canshu_range_outer:
            result_outer = compare_duandian(func, dingyi_interval, canshu_range_outer, canshu_symbol, min_max)
            result.update(result_outer)

        return result


def _minmax_value_hancanshu(func, deriv_solver, canshu_symbol, canshu_interval, dingyi_interval, min_max):
    """求含参数 func 的最值"""

    expr, var = func.expression, func.var
    result = dict()

    # 1. 如果极值点不存在，比较端点大小
    if deriv_solver is None:
        return compare_duandian(func, dingyi_interval, canshu_interval, canshu_symbol, min_max)

    # 2. 如果极值点存在，比较端点和极值点大小，讨论极值点是否在区间内部！
    canshu_range_union = EmptySet()
    # {m: {0}, (m, x): {((-oo, 0), -2), ((0, oo), -2)}}
    for prmt, vlus in deriv_solver.items():
        # (m, x): {((-oo, 0), -2), ((0, oo), -2)} 格式
        if isinstance(prmt, tuple):
            for vlu in vlus:
                parmt_vlu = dict(zip(prmt, vlu))
                canshu_range = parmt_vlu[canshu_symbol]  # 区间 / 值
                var_x = parmt_vlu[var]

                if canshu_range.is_real:
                    canshu_range_set = FiniteSet(canshu_range)  # 值 => FiniteSet
                    # 补充：如果 x 是区间，参数 a 是值，当做 m: {0} 格式 处理，
                    if is_interval(var_x):
                        # 把参数代入 f(x)， f(x) 一定是参数值
                        constant_value = expr.subs(canshu_symbol, canshu_range)
                        result_piece = {canshu_range_set: [constant_value, (None, True)]}
                        result.update(result_piece)

                        canshu_range_union = canshu_range_union.union(canshu_range_set)

                else:
                    # print "当 %s ∈ %s 时：" % (canshu_symbol, canshu_range)

                    # 比较端点和极值点
                    result_piece = compare_duandian_jizhi(func, dingyi_interval, var_x,
                                                          canshu_symbol, canshu_range, min_max)

                    result.update(result_piece)

                    # print "函数 %s 值为 %s" % (str(min_max), result_piece)

                    # 已经考察过的参数区间
                    canshu_range_union = canshu_range_union.union(canshu_range)

        # m: {0} 格式
        else:
            # x: {x0, x1, ...}
            if prmt != canshu_symbol:
                for vlu in vlus:
                    result_piece = compare_duandian_jizhi(func, dingyi_interval, vlu,
                                                          canshu_symbol, canshu_interval, min_max)
                    result.update(result_piece)
                canshu_range_union = S.Reals

            # m 是一个或多个值
            else:
                for vlu in vlus:
                    # 把参数代入 f(x)， f(x) 一定是参数值
                    constant_value = expr.subs(canshu_symbol, vlu)
                    # print "当 %s = %s 时，f(x) = %s，函数最值为 %s " % \
                    #       (canshu_symbol, vlu, constant_value, constant_value)
                    result_piece = {FiniteSet(vlu): [constant_value, (None, True)]}
                    result.update(result_piece)

                canshu_range_union = canshu_range_union.union(vlus)

    # 考察参数取值范围是否全部讨论过
    # 解 f' = 0 Mathematica 的输出如果没有包含 a 的剩余取值范围 A，那么函数在 A 上单调，只要考虑端点
    canshu_interval_complement = canshu_range_union.complement(canshu_interval)
    if canshu_interval_complement:
        # 补充：如果 canshu_interval_complement 是并集，在单个区间上分别计算
        if isinstance(canshu_interval_complement, Union):
            for qj in canshu_interval_complement.args:
                result_piece = compare_duandian(func, dingyi_interval, qj, canshu_symbol, min_max)
                result.update(result_piece)
        else:
            result_piece = compare_duandian(func, dingyi_interval, canshu_interval_complement, canshu_symbol, min_max)
            result.update(result_piece)

    return result


max_char_map = {Max: "最大值", Min: "最小值"}
max_char_map2 = {Max: "上限", Min: "下限"}
max_cal_map = {Max: max, Min: min}


class MaxValue(BaseFunction):
    """利用导数计算函数最大值 （含参数、不含参数）
    :input BaseFunc, Interval (optional)
    :return 如果含参数 {canshu_range: [min_max_val, (x_val, reacheable)], ... }
            如果不含参数 [min_max_val, (x_val, reacheable)] """
    min_max = Max
    canshu = None
    var = None

    def process_input(self, args):
        """处理输入"""
        func = check_func(args[0])
        expr, var = func.expression, func.var
        self.var = var
        if len(args) == 2:  # 如果给定区间，求在区间上的最值
            dingyi_interval = check_inter(args[1])
        elif func.dingyiyu:  # 区间可能放在 BaseFunc 的 dingyiyu 属性里面
            dingyi_interval = check_inter(func.dingyiyu)
        else:  # 否则求函数定义域，求在定义域上的最值
            dingyi_interval = self.search(var)
            if not dingyi_interval:
                dingyi_interval = QiuDingYiYu().solver(func)

        # 考察表达式含不含参数
        if is_piecefunc(func):
            canshu_symbol = list(set(chain(*[expr_1.free_symbols for expr_1, _ in expr])) - {var})
        else:
            canshu_symbol = list(expr.free_symbols - {var})

        # 如果含有参数，search 参数范围，如果 search 不到，默认参数属于 R
        canshu_interval = S.Reals
        has_canshu = False
        if canshu_symbol:
            canshu_symbol.sort(key=lambda arg: str(arg))
            canshu_symbol = canshu_symbol[0]
            self.canshu = canshu_symbol
            has_canshu = True
            search_canshu_interval = self.search(canshu_symbol)
            if search_canshu_interval:
                canshu_interval = search_canshu_interval

        # 补充：如果区间含参数，区间定义求参数
        if len(args) == 2 and dingyi_interval.free_symbols:
            has_canshu = True
            if not canshu_symbol:
                canshu_symbol = dingyi_interval.free_symbols.pop()
                self.canshu = canshu_symbol
            from mathsolver.functions.hanshu.qujian import QuJianQiuCan
            from mathsolver.functions.jihe.jihe_solve import JiHeSolve
            canshu_interval = QuJianQiuCan().solver(dingyi_interval).output[0].value.values()[0]
            # 补充，含参数区间属于定义域
            def_domain = QuJianQiuCan().solver(func)
            inter2 = JiHeSolve()._solve_child_unival(dingyi_interval, def_domain)
            canshu_interval = canshu_interval.intersect(inter2)

        # 补充：如果区间含有参数
        return expr, var, dingyi_interval, has_canshu, canshu_symbol, canshu_interval

    def _steps_nocanshu(self, result):
        """根据最终结果，添加输出步骤（不含参数）"""
        if result[1][1]:
            if result[1][0]:
                steps = [["", "所以函数在 %s= %s 处取到 %s %s" %
                          (new_latex(self.var), result[1][0], max_char_map[self.min_max], new_latex(result[0]))]]
            else:  # 函数在 x=None处取得最值
                steps = [["", "所以函数的%s为 %s" %
                          (max_char_map[self.min_max], new_latex(result[0]))]]

        else:
            steps = [["", "所以函数最值不存在，在 %s= %s 处的%s为 %s" %
                      (new_latex(self.var), result[1][0], max_char_map2[self.min_max], new_latex(result[0]))]]
        return steps

    def _steps_canshu(self, result):
        """根据最终结果，添加输出步骤（含参数）"""
        steps = []
        for _canshu_fanwei, _other in result.items():
            if _other[1][1]:
                steps.append(["", "%s ∈ %s 时，函数%s为 %s" %
                              (str(self.canshu), new_latex(_canshu_fanwei), max_char_map[self.min_max],
                               new_latex(_other[0]))])
            else:
                steps.append(["", "%s ∈ %s 时，函数%s不存在，%s为 %s" %
                              (str(self.canshu), new_latex(_canshu_fanwei), max_char_map[self.min_max],
                               max_char_map2[self.min_max], new_latex(_other[0]))])
        return steps

    def zuizhi_sumabsfunc(self, func):
        """绝对值相加函数的最值"""
        sumexpr = func.expression
        low_n, up_n = sumexpr.args[1][1:]
        if (up_n - low_n) % 2 == 0:
            value = (low_n + up_n) / 2
            min_xvalue = (low_n + up_n) / 2
        else:
            value = (low_n + up_n) / 2
            min_xvalue = Interval(value, value + 1)
        if self.min_max == Max:
            result = [oo, (oo, False)]
            self.steps.append(["", "%s 的最大值为 %s" % (new_latex(sumexpr), new_latex(oo))])
        else:
            min_value = sumexpr.subs(func.var, value).doit()
            result = [min_value, [min_xvalue, True]]
            self.steps.append(
                ["", "%s 在 %s 取到最小值 %s" % (new_latex(sumexpr), new_latex(min_xvalue), new_latex(min_value))])
        self.output.append(BaseValue(result))
        return self

    def solver(self, *args):
        func = check_func(args[0])
        expr, var, dingyi_interval, has_canshu, canshu_symbol, canshu_interval = self.process_input(args)
        self.label = {"利用导数计算函数最值", }

        # 补充1：常数函数 => 返回常数
        if is_constantfunc(func):
            result = [func.expression, [dingyi_interval, True]]
            self.steps.append(["", "常函数 %s 的最值恒为 %s" % (func.printing(), func.expression)])
            self.output.append(BaseValue(result))
            return self

        # 补充2：绝对值相加函数
        if is_sumabsfunc(func):
            return self.zuizhi_sumabsfunc(func)

        # 补充3：绝对值函数 => 分段函数
        if is_absfunc(func):
            # 补充：含参数的绝对值最值调用 zuizhi_abs_hancan
            if has_canshu:
                result, steps = zuizhi_abs_hancan(self.min_max, func, dingyi_interval, canshu_symbol, canshu_interval)
                self.steps.extend(steps)
                if isinstance(result, list):
                    self.steps.extend(self._steps_nocanshu(result))
                else:
                    self.steps.extend(self._steps_canshu(result))
                self.output.append(BaseValue(result))
                return self

            # 绝对值化简为分段函数
            else:
                abs_solver = QuJueDuiZhi().solver(func)
                self.steps.extend(abs_solver.steps)
                expr = abs_solver.output
                func = BasePieceFunc({"var": func.var, "name": func.name, "type": "", "expression": expr})

        # 补充4：分段函数：计算每个分段上面的最值，然后合并
        # TODO: 含参数的先不考虑
        if is_piecefunc(func):
            self.steps.append(["", "考虑函数在每个分段上的最值"])
            minmax_pieces = []
            for piece_exp, piece_interval in expr:
                self.steps.append(["", "当 %s ∈ %s 时，函数表达式为 %s" %
                                   (new_latex(var), new_latex(piece_interval), new_latex(piece_exp))])
                piece_solver = self.__class__().solver(
                    BaseFunc({"var": var, "name": func.name, "type": "", "expression": piece_exp}), piece_interval)
                self.steps.extend(piece_solver.steps)
                minmax_pieces.append(piece_solver.output[0].value)
            # 求最小值，最小值相等，reacheable = True 选中；求最大值，最大值相等，reacheable = True 选中
            minmax_func = max_cal_map[self.min_max]
            helper_map = {min: -1, max: 1}
            result = minmax_func(minmax_pieces, key=lambda item: item[0] + helper_map[minmax_func] * item[1][1])

            self.output.append(BaseValue(result))
            self.steps.extend(self._steps_nocanshu(result))
            return self

        # 1. 求导计算极值

        # 求 f 的导数 f'
        deriv_func = HanShuDaoShuYunSuan().solver(func).output[0]
        self.steps.append(["", "对函数 %s 求导得: %s" % (func.printing(), new_latex(deriv_func.expression))])

        # 参数和变量的范围
        constrain_dict = {deriv_func.var: dingyi_interval}
        if has_canshu:
            constrain_dict.update({canshu_symbol: canshu_interval})

        # 计算 f' = 0 在 区间上的解
        try:
            deriv_solver_symbolvalue = MathematicaSolve().solver(BaseEq([deriv_func.expression, 0]),
                                                                 BaseVariable([deriv_func.var]),
                                                                 constrain_dict).output[0]
            deriv_solver = deriv_solver_symbolvalue.value
            # 如果 Mathematic 解不出，当做无解，
            if deriv_solver and list(deriv_solver.values())[0]:
                has_solution = True
            else:
                has_solution = False
        except Exception:
            has_solution = False

        # 如果极值点存在
        if has_solution:
            # 补充：对于像 {(x, a): {(2, (1, oo)), (2*a, (1, oo))}} 格式的结果，合并 2, 2*a
            deriv_solver = process_m_eqsolver_output(deriv_solver, deriv_func.var)
            self.steps.append(["", "导函数等于零的解集为 %s，即函数的极值点" % deriv_solver_symbolvalue.printing()])
            self.steps.append(["", "比较函数在极值点和区间端点处的值得："])
            has_jizhidian = True
        # 如果 f'(x) = 0 无解，则极值点不存在
        else:
            self.steps.append(["", "导函数等于零无解，极值点不存在"])
            self.steps.append(["", "比较函数在区间端点处的最值得："])
            deriv_solver = None
            has_jizhidian = False

        # 2. 计算最值
        # 2.1 不含参数
        if not has_canshu:
            bound_values = interval_bound_val(func, dingyi_interval)
            # 如果没有极值点，比较端点大小；如果极值点存在，且不含参数，比较端点和极值点大小
            if has_jizhidian:
                ans_finite_set = deriv_solver.get(deriv_func.var)
                # 补充：如果输入为 BaseSineq，则求 n ∈ Z，上面的最值
                if len(args) > 1 and isinstance(args[1], BaseSinEq):
                    assert dingyi_interval == Interval(1, oo), "输入必须为 n >= 1"
                    for an in ans_finite_set:
                        bound_values.update({expr.subs(var, floor(an)): (floor(an), True)})
                        bound_values.update({expr.subs(var, ceiling(an)): (ceiling(an), True)})
                else:
                    for an in ans_finite_set:
                        bound_values.update({expr.subs(var, an): (an, True)})

            zuizhi = self.min_max(*bound_values.keys())
            result = [zuizhi, bound_values[zuizhi]]  # 结果 [最值，(x的值，取不取得到)]
            self.output.append(BaseValue(result))
            self.steps.extend(self._steps_nocanshu(result))

        # 2.2 含参数
        else:
            result = _minmax_value_hancanshu(func, deriv_solver,
                                             canshu_symbol, canshu_interval, dingyi_interval, self.min_max)

            self.output.append(BaseValue(result))
            self.steps.extend(self._steps_canshu(result))

        return self


class MinValue(MaxValue):
    min_max = Min


class MinXValue(BaseFunction):
    """函数在何处取得最小值，输出函数最小值
    
    :return 如果含参数 BaseSymbolValue({(a, x): ((interval_a, x_value), ...)})
            如果不含参数 BaseSymbolValue({x: x_value})
    """
    func = MinValue

    def solver(self, *args):
        zuizhi_solver = self.func(self.known).solver(*args)
        self.steps = zuizhi_solver.steps
        solver_output = zuizhi_solver.output[0].value

        if isinstance(solver_output, dict):
            inter_a_x = []
            for canshu_range, bunch in solver_output.items():
                _, (x_val, reacheable) = bunch
                if reacheable:
                    inter_a_x.append((canshu_range, x_val))
            self.output.append(BaseSymbolValue({(zuizhi_solver.canshu, zuizhi_solver.var): tuple(inter_a_x)}))

        else:
            _, (x_val, reacheable) = solver_output
            if reacheable:
                self.output.append(BaseSymbolValue({zuizhi_solver.var: x_val}))

        return self


class MaxXValue(MinXValue):
    func = MaxValue


def _steps_canshu_combine(result, canshu):
    """合并后含参数最大最小值输出步骤
        1. 如果 result 含有参数 result =  {canshu_range: [[min_val, (x_val, reacheable)],
                                           [max_val, (x_val, reacheable)]...}
        2. 如果不含参数 result = [[min_val, (x_val, reacheable)], [max_val, (x_val, reacheable)]
    """

    steps = [["", "综上所述："]]

    if isinstance(result, dict):
        for _canshu_fanwei, _other in result.items():
            if _other[0][1][1]:
                buzou = "%s ∈ %s 时，函数最小值为 %s，" % (str(canshu), new_latex(_canshu_fanwei), new_latex(_other[0][0]))

            else:
                buzou = "%s ∈ %s 时，函数最小值不存在，下限为 %s" % (str(canshu), new_latex(_canshu_fanwei), new_latex(_other[0][0]))

            if _other[1][1][1]:
                buzou += "%s ∈ %s 时，函数最大值为 %s" % (str(canshu), new_latex(_canshu_fanwei), new_latex(_other[1][0]))
            else:
                buzou += "%s ∈ %s 时，函数最大值不存在，上限为 %s" % (str(canshu), new_latex(_canshu_fanwei), new_latex(_other[1][0]))

    else:
        (min_val, (min_xval, min_reacheable)), (max_val, (max_xval, max_reacheable)) = result

        if min_reacheable:
            buzou = "函数的最小值为 %s，" % min_val
        else:
            buzou = "函数的下限为 %s，" % min_val

        if max_reacheable:
            buzou += "函数的最大值为 %s" % max_val
        else:
            buzou += "函数的上限为 %s" % max_val

        steps.append(['', buzou])

    return steps


def min_max_combine_case1(min_output, max_output):
    """min_value, max_value 两个都含有参数"""
    assert isinstance(max_output, dict) and isinstance(min_output, dict)
    result = {}
    singlevalue_result = {}
    # 计算参数讨论范围的交
    for max_inter, max_others in max_output.items():
        for min_inter, min_others in min_output.items():
            min_max_intersect = max_inter.intersect(min_inter)
            if min_max_intersect:
                if is_interval(min_max_intersect):
                    result[min_max_intersect] = [min_others, max_others]
                # 待补充：如果是 FiniteSet，可以将结果合并到其中一个区间
                if isinstance(min_max_intersect, FiniteSet):
                    singlevalue_result[min_max_intersect] = [min_others, max_others]
            else:
                continue  # 如果没有交集，不讨论

    # 如果是 FiniteSet
    if singlevalue_result:
        for valueset, _others in singlevalue_result.items():
            already_contained = any(valueset.is_subset(item) for item in result.keys())
            if already_contained:
                continue
            else:
                result[valueset] = _others
    return result


def min_max_combine(min_output, max_output, canshu=None):
    """含参数最值输出的最大最小值合并
    
    :input  含参数最大值输出结果： {canshu_range: [min_max_val, (x_val, reacheable)], ... }
            含参数最小值输出结果： {canshu_range: [min_max_val, (x_val, reacheable)], ... }
            不含参数最大最小输出结果：[min_max_val, (x_val, reacheable)]
            
    :return {canshu_range: [[min_val, (x_val, reacheable)], 
                            [max_val, (x_val, reacheable)]... }
            或者 [[min_val, (x_val, reacheable)], [max_val, (x_val, reacheable)]]
    """

    # min_value ，max_value 一个含参数，另外一个不含参数，或者两个都不含参数
    result = {}
    if isinstance(max_output, list) and isinstance(min_output, dict):
        for min_inter, min_others in min_output.items():
            result[min_inter] = [min_others, max_output]
    elif isinstance(min_output, list) and isinstance(max_output, dict):
        for max_inter, max_others in max_output.items():
            result[max_inter] = [min_output, max_others]
    elif isinstance(min_output, list) and isinstance(max_output, list):
        result = [min_output, max_output]
    else:
        result = min_max_combine_case1(min_output, max_output)

    # 步骤
    steps = _steps_canshu_combine(result, canshu)
    return result, steps


class MinMaxValue(BaseFunction):
    """求函数的最值
    输出：1. 如果不含参数：[[min_val, (x_val, reacheable)], [max_val, (x_val, reacheable)]
         2. 如果含有参数：{canshu_range: [[min_val, (x_val, reacheable)], [max_val, (x_val, reacheable)]... } """
    canshu = None

    def solver(self, *args):
        # 计算最小值
        min_solver = MinValue(self.known).solver(*args)
        self.steps.extend(min_solver.steps)
        min_output = min_solver.output[0].value

        # 计算最大值
        max_solver = MaxValue(self.known).solver(*args)
        self.steps.extend(max_solver.steps)
        max_output = max_solver.output[0].value

        self.canshu = min_solver.canshu

        # 合并最小最大值
        combined_output, min_max_steps = min_max_combine(min_output, max_output, canshu=self.canshu)
        self.steps.extend(min_max_steps)
        self.output.append(BaseValue(combined_output))
        self.label.add("求函数的最值")
        return self


class FunctionRange0(MaxValue):
    """求函数值域 （不含参数，含参数）
        1. 利用导数计算函数最值
        2. 合并最 大/小 值
    输出：
        1. 含参数：{canshu_range: range, ... }
        2. 不含参数：range"""

    def solver(self, *args):
        minmax_solver = MinMaxValue(self.known).solver(*args)
        self.steps.extend(minmax_solver.steps)
        combined_output = minmax_solver.output[0].value

        # 如果不含参数：[[min_val, (x_val, reacheable)], [max_val, (x_val, reacheable)]
        if isinstance(combined_output, list):
            (min_val, (min_xval, min_reacheable)), (max_val, (max_xval, max_reacheable)) = combined_output
            function_range = Interval(min_val, max_val, not min_reacheable, not max_reacheable)
            self.steps.append(['', "函数值域为：%s" % new_latex(function_range)])

        # 如果含有参数：{canshu_range: [[min_val, (x_val, reacheable)], [max_val, (x_val, reacheable)]... }
        else:
            function_range = {}
            for _inter, _others in combined_output.items():
                (min_val, (min_xval, min_reacheable)), (max_val, (max_xval, max_reacheable)) = _others
                piece_range = Interval(min_val, max_val, not min_reacheable, not max_reacheable)
                function_range[_inter] = piece_range
                self.steps.append(['', "当 %s ∈ %s 时，函数值域为 %s" %
                                   (new_latex(minmax_solver.canshu), new_latex(_inter), new_latex(piece_range))])

        self.output.append(BaseValue(function_range))
        self.label.add("求函数值域")
        return self


class FunctionRange(MaxValue):
    """求函数的值域 （不含参数，含参数）
        
        1. 利用导数计算函数在单个区间 (Interval) 上最值
        2. 合并最 大/小 值
        *3. 含参数的合并暂不考虑，
    
    与 Function_range0 区别：
        不含参数，定义区间是 Union 时，计算单个 Interval 上的值域，输出值域的并集
    
    输出：
        1. 含参数：{canshu_range: range, ... }
        2. 不含参数：range"""

    def solver(self, *args):
        func = check_func(args[0])
        expr, var, dingyi_interval, has_canshu, canshu_symbol, canshu_interval = self.process_input(args)

        # 不含参数，定义区间是 Union 时，计算单个 Interval 上的值域，输出值域的并集
        if not has_canshu and isinstance(dingyi_interval, Union):
            self.steps.append(["", "定义区间为并集，分别计算函数在每个子集上的值域"])
            final_range = []
            for interval in dingyi_interval.args:
                f_range = FunctionRange().solver(func, interval).output[0].value
                self.steps.append(["", "函数在区间 %s 的值域为 %s" % (new_latex(interval), new_latex(f_range))])
                final_range.append(f_range)
            function_range = Union(*final_range)

            self.output.append(BaseValue(function_range))
            self.steps.append(["", "所以函数在区间 %s 上的值域为 %s" % (new_latex(dingyi_interval), new_latex(function_range))])
            self.label.add("求函数的值域")
            return self
        else:
            return FunctionRange0().solver(*args)


if __name__ == '__main__':
    pass
